/*
 * Copyright (c) [2021] Huawei Technologies Co.,Ltd.All rights reserved.
 *
 * OpenArkCompiler is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
 * FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/


public class CheckLoops {
    private static int sResult;
    //
    // Various sequence variables used in bound checks.
    //
    // CHECK-START: void Main.bubble(int[]) BCE (before)
    // CHECK-DAG: BoundsCheck
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.bubble(int[]) BCE (after)
    // CHECK-NOT: BoundsCheck
    private static void bubble(int[] a) {
        for (int i = a.length; --i >= 0; ) {
            for (int j = 0; j < i; j++) {
                if (a[j] > a[j + 1]) {
                    int tmp = a[j];
                    a[j] = a[j + 1];
                    a[j + 1] = tmp;
                }
            }
        }
    }
    // CHECK-START: int Main.periodicIdiom(int) BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int Main.periodicIdiom(int) BCE (after)
    // CHECK-NOT: BoundsCheck
    // CHECK-NOT: Deoptimize
    private static int periodicIdiom(int tc) {
        int[] x = {1, 3};
        // Loop with periodic sequence (0, 1).
        int k = 0;
        int result = 0;
        for (int i = 0; i < tc; i++) {
            result += x[k];
            k = 1 - k;
        }
        return result;
    }
    // CHECK-START: int Main.periodicSequence2(int) BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int Main.periodicSequence2(int) BCE (after)
    // CHECK-NOT: BoundsCheck
    // CHECK-NOT: Deoptimize
    private static int periodicSequence2(int tc) {
        int[] x = {1, 3};
        // Loop with periodic sequence (0, 1).
        int k = 0;
        int l = 1;
        int result = 0;
        for (int i = 0; i < tc; i++) {
            result += x[k];
            int t = l;
            l = k;
            k = t;
        }
        return result;
    }
    // CHECK-START: int Main.periodicSequence4(int) BCE (before)
    // CHECK-DAG: BoundsCheck
    // CHECK-DAG: BoundsCheck
    // CHECK-DAG: BoundsCheck
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int Main.periodicSequence4(int) BCE (after)
    // CHECK-NOT: BoundsCheck
    // CHECK-NOT: Deoptimize
    private static int periodicSequence4(int tc) {
        int[] x = {1, 3, 5, 7};
        // Loop with periodic sequence (0, 1, 2, 3).
        int k = 0;
        int l = 1;
        int m = 2;
        int n = 3;
        int result = 0;
        for (int i = 0; i < tc; i++) {
            result += x[k] + x[l] + x[m] + x[n]; // all used at once
            int t = n;
            n = k;
            k = l;
            l = m;
            m = t;
        }
        return result;
    }
    // CHECK-START: int Main.justRightUp1() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int Main.justRightUp1() BCE (after)
    // CHECK-NOT: BoundsCheck
    // CHECK-NOT: Deoptimize
    private static int justRightUp1() {
        int[] x = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int result = 0;
        for (int i = Integer.MAX_VALUE - 10, k = 0; i < Integer.MAX_VALUE; i++) {
            result += x[k++];
        }
        return result;
    }
    // CHECK-START: int Main.justRightUp2() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int Main.justRightUp2() BCE (after)
    // CHECK-NOT: BoundsCheck
    // CHECK-NOT: Deoptimize
    private static int justRightUp2() {
        int[] x = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int result = 0;
        for (int i = Integer.MAX_VALUE - 10; i < Integer.MAX_VALUE; i++) {
            result += x[i - Integer.MAX_VALUE + 10];
        }
        return result;
    }
    // CHECK-START: int Main.justRightUp3() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int Main.justRightUp3() BCE (after)
    // CHECK-NOT: BoundsCheck
    // CHECK-NOT: Deoptimize
    private static int justRightUp3() {
        int[] x = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int result = 0;
        for (int i = Integer.MAX_VALUE - 10, k = 0; i <= Integer.MAX_VALUE - 1; i++) {
            result += x[k++];
        }
        return result;
    }
    // CHECK-START: int Main.justOOBUp() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int Main.justOOBUp() BCE (after)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int Main.justOOBUp() BCE (after)
    // CHECK-NOT: Deoptimize
    private static int justOOBUp() {
        int[] x = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int result = 0;
        // Infinite loop!
        for (int i = Integer.MAX_VALUE - 9, k = 0; i <= Integer.MAX_VALUE; i++) {
            result += x[k++];
        }
        return result;
    }
    // CHECK-START: int Main.justRightDown1() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int Main.justRightDown1() BCE (after)
    // CHECK-NOT: BoundsCheck
    // CHECK-NOT: Deoptimize
    private static int justRightDown1() {
        int[] x = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int result = 0;
        for (int i = Integer.MIN_VALUE + 10, k = 0; i > Integer.MIN_VALUE; i--) {
            result += x[k++];
        }
        return result;
    }
    // CHECK-START: int Main.justRightDown2() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int Main.justRightDown2() BCE (after)
    // CHECK-NOT: BoundsCheck
    // CHECK-NOT: Deoptimize
    private static int justRightDown2() {
        int[] x = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int result = 0;
        for (int i = Integer.MIN_VALUE + 10; i > Integer.MIN_VALUE; i--) {
            result += x[Integer.MAX_VALUE + i];
        }
        return result;
    }
    // CHECK-START: int Main.justRightDown3() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int Main.justRightDown3() BCE (after)
    // CHECK-NOT: BoundsCheck
    // CHECK-NOT: Deoptimize
    private static int justRightDown3() {
        int[] x = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int result = 0;
        for (int i = Integer.MIN_VALUE + 10, k = 0; i >= Integer.MIN_VALUE + 1; i--) {
            result += x[k++];
        }
        return result;
    }
    // CHECK-START: int Main.justOOBDown() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int Main.justOOBDown() BCE (after)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int Main.justOOBDown() BCE (after)
    // CHECK-NOT: Deoptimize
    private static int justOOBDown() {
        int[] x = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int result = 0;
        // Infinite loop!
        for (int i = Integer.MIN_VALUE + 9, k = 0; i >= Integer.MIN_VALUE; i--) {
            result += x[k++];
        }
        return result;
    }
    // CHECK-START: void Main.lowerOOB(int[]) BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.lowerOOB(int[]) BCE (after)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.lowerOOB(int[]) BCE (after)
    // CHECK-NOT: Deoptimize
    private static void lowerOOB(int[] x) {
        // OOB!
        for (int i = -1; i < x.length; i++) {
            sResult += x[i];
        }
    }
    // CHECK-START: void Main.upperOOB(int[]) BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.upperOOB(int[]) BCE (after)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.upperOOB(int[]) BCE (after)
    // CHECK-NOT: Deoptimize
    private static void upperOOB(int[] x) {
        // OOB!
        for (int i = 0; i <= x.length; i++) {
            sResult += x[i];
        }
    }
    // CHECK-START: void Main.doWhileUpOOB() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.doWhileUpOOB() BCE (after)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.doWhileUpOOB() BCE (after)
    // CHECK-NOT: Deoptimize
    private static void doWhileUpOOB() {
        int[] x = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int i = 0;
        // OOB!
        do {
            sResult += x[i++];
        } while (i <= x.length);
    }
    // CHECK-START: void Main.doWhileDownOOB() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.doWhileDownOOB() BCE (after)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.doWhileDownOOB() BCE (after)
    // CHECK-NOT: Deoptimize
    private static void doWhileDownOOB() {
        int[] x = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int i = x.length - 1;
        // OOB!
        do {
            sResult += x[i--];
        } while (-1 <= i);
    }
    // CHECK-START: void Main.hiddenOOB1(int) BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.hiddenOOB1(int) BCE (after)
    // CHECK-DAG: Deoptimize
    //
    // CHECK-START: void Main.hiddenOOB1(int) BCE (after)
    // CHECK-NOT: BoundsCheck
    private static void hiddenOOB1(int lo) {
        int[] a = {1};
        for (int i = lo; i <= 10; i++) {
            // Dangerous loop where careless static range analysis would yield strict upper bound
            // on index j of 5. When, for instance, lo and thus i = -2147483648, the upper bound
            // becomes really positive due to arithmetic wrap-around, causing OOB.
            // Dynamic BCE is feasible though, since it checks the range.
            for (int j = 4; j < i - 5; j++) {
                sResult += a[j - 4];
            }
        }
    }
    // CHECK-START: void Main.hiddenOOB2(int) BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.hiddenOOB2(int) BCE (after)
    // CHECK-DAG: Deoptimize
    //
    // CHECK-START: void Main.hiddenOOB2(int) BCE (after)
    // CHECK-NOT: BoundsCheck
    private static void hiddenOOB2(int hi) {
        int[] a = {1};
        for (int i = 0; i < hi; i++) {
            // Dangerous loop where careless static range analysis would yield strict lower bound
            // on index j of 5. When, for instance, hi and thus i = 2147483647, the upper bound
            // becomes really negative due to arithmetic wrap-around, causing OOB.
            // Dynamic BCE is feasible though, since it checks the range.
            for (int j = 6; j > i + 5; j--) {
                sResult += a[j - 6];
            }
        }
    }
    // CHECK-START: void Main.hiddenInfiniteOOB() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.hiddenInfiniteOOB() BCE (after)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.hiddenInfiniteOOB() BCE (after)
    // CHECK-NOT: Deoptimize
    private static void hiddenInfiniteOOB() {
        int[] a = {11};
        for (int i = -1; i <= 0; i++) {
            // Dangerous loop where careless static range analysis would yield a safe upper bound
            // of -3. In reality, due to arithmetic wrap-around (when i = -1, j <= 2147483647;
            // whereas when i = 0, j <= -3), this is an infinite loop that goes OOB.
            for (int j = -3; j <= 2147483646 * i - 3; j++) {
                sResult += a[j + 3];
            }
        }
    }
    // CHECK-START: void Main.hiddenFiniteOOB() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.hiddenFiniteOOB() BCE (after)
    // CHECK-DAG: Deoptimize
    //
    // CHECK-START: void Main.hiddenFiniteOOB() BCE (after)
    // CHECK-NOT: BoundsCheck
    private static void hiddenFiniteOOB() {
        int[] a = {111};
        for (int i = -1; i <= 0; i++) {
            // Dangerous loop similar as above where the loop is now finite, but the
            // loop still goes out of bounds for i = -1 due to the large upper bound.
            // Dynamic BCE is feasible though, since it checks the range.
            for (int j = -4; j < 2147483646 * i - 3; j++) {
                sResult += a[j + 4];
            }
        }
    }
    // CHECK-START: void Main.inductionOOB(int[]) BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.inductionOOB(int[]) BCE (after)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.inductionOOB(int[]) BCE (after)
    // CHECK-NOT: Deoptimize
    private static void inductionOOB(int[] a) {
        // Careless range analysis would remove the bounds check.
        // However, the narrower induction b wraps around arithmetically
        // before it reaches the end of arrays longer than 127.
        byte b = 0;
        for (int i = 0; i < a.length; i++) {
            a[b++] = i;
        }
    }
    // CHECK-START: void Main.controlOOB(int[]) BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.controlOOB(int[]) BCE (after)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.controlOOB(int[]) BCE (after)
    // CHECK-NOT: Deoptimize
    private static void controlOOB(int[] a) {
        // As above, but now the loop control also wraps around.
        for (byte i = 0; i < a.length; i++) {
            a[i] = -i;
        }
    }
    // CHECK-START: void Main.conversionOOB(int[]) BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.conversionOOB(int[]) BCE (after)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: void Main.conversionOOB(int[]) BCE (after)
    // CHECK-NOT: Deoptimize
    private static void conversionOOB(int[] a) {
        // As above, but with wrap around caused by an explicit conversion.
        for (int i = 0; i < a.length; ) {
            a[i] = i;
            i = (byte) (i + 1);
        }
    }
    // CHECK-START: int[] Main.add() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int[] Main.add() BCE (after)
    // CHECK-NOT: BoundsCheck
    // CHECK-NOT: Deoptimize
    private static int[] add() {
        int[] a = new int[10];
        for (int i = 0; i <= 3; i++) {
            for (int j = 0; j <= 6; j++) {
                a[i + j] += 1;
            }
        }
        return a;
    }
    // CHECK-START: int[] Main.multiply1() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int[] Main.multiply1() BCE (after)
    // CHECK-NOT: BoundsCheck
    // CHECK-NOT: Deoptimize
    private static int[] multiply1() {
        int[] a = new int[10];
        try {
            for (int i = 0; i <= 3; i++) {
                for (int j = 0; j <= 3; j++) {
                    // Range [0,9]: safe.
                    a[i * j] += 1;
                }
            }
        } catch (Exception e) {
            a[0] += 1000;
        }
        return a;
    }
    // CHECK-START: int[] Main.multiply2() BCE (before)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int[] Main.multiply2() BCE (after)
    // CHECK-DAG: BoundsCheck
    //
    // CHECK-START: int[] Main.multiply2() BCE (after)
    // CHECK-NOT: Deoptimize
    private static int[] multiply2() {
        int[] a = new int[10];
        try {
            for (int i = -3; i <= 3; i++) {
                for (int j = -3; j <= 3; j++) {
                    // Range [-9,9]: unsafe.
                    a[i * j] += 1;
                }
            }
        } catch (Exception e) {
            a[0] += 1000;
        }
        return a;
    }
    // CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (before)
    // CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
    // CHECK-DAG: NullCheck   loop:<<Loop>>
    // CHECK-DAG: ArrayLength loop:<<Loop>>
    // CHECK-DAG: BoundsCheck loop:<<Loop>>
    //
    // CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after)
    // CHECK-DAG: ArrayGet    loop:{{B\d+}}
    // CHECK-DAG: Deoptimize  loop:none
    //
    // CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after)
    // CHECK-NOT: NullCheck   loop:{{B\d+}}
    // CHECK-NOT: ArrayLength loop:{{B\d+}}
    // CHECK-NOT: BoundsCheck loop:{{B\d+}}
    private static int linearDynamicBCE1(int[] x, int lo, int hi) {
        int result = 0;
        for (int i = lo; i < hi; i++) {
            sResult += x[i];
        }
        return result;
    }
    // CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (before)
    // CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
    // CHECK-DAG: NullCheck   loop:<<Loop>>
    // CHECK-DAG: ArrayLength loop:<<Loop>>
    // CHECK-DAG: BoundsCheck loop:<<Loop>>
    //
    // CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after)
    // CHECK-DAG: ArrayGet    loop:{{B\d+}}
    // CHECK-DAG: Deoptimize  loop:none
    //
    // CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after)
    // CHECK-NOT: NullCheck   loop:{{B\d+}}
    // CHECK-NOT: ArrayLength loop:{{B\d+}}
    // CHECK-NOT: BoundsCheck loop:{{B\d+}}
    private static int linearDynamicBCE2(int[] x, int lo, int hi, int offset) {
        int result = 0;
        for (int i = lo; i < hi; i++) {
            sResult += x[offset + i];
        }
        return result;
    }
    // CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (before)
    // CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
    // CHECK-DAG: NullCheck   loop:<<Loop>>
    // CHECK-DAG: ArrayLength loop:<<Loop>>
    // CHECK-DAG: BoundsCheck loop:<<Loop>>
    //
    // CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after)
    // CHECK-DAG: ArrayGet    loop:{{B\d+}}
    // CHECK-DAG: Deoptimize  loop:none
    //
    // CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after)
    // CHECK-NOT: NullCheck   loop:{{B\d+}}
    // CHECK-NOT: ArrayLength loop:{{B\d+}}
    // CHECK-NOT: BoundsCheck loop:{{B\d+}}
    private static int wrapAroundDynamicBCE(int[] x) {
        int w = 9;
        int result = 0;
        for (int i = 0; i < 10; i++) {
            result += x[w];
            w = i;
        }
        return result;
    }
    // CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (before)
    // CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
    // CHECK-DAG: NullCheck   loop:<<Loop>>
    // CHECK-DAG: ArrayLength loop:<<Loop>>
    // CHECK-DAG: BoundsCheck loop:<<Loop>>
    //
    // CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after)
    // CHECK-DAG: ArrayGet    loop:{{B\d+}}
    // CHECK-DAG: Deoptimize  loop:none
    //
    // CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after)
    // CHECK-NOT: NullCheck   loop:{{B\d+}}
    // CHECK-NOT: ArrayLength loop:{{B\d+}}
    // CHECK-NOT: BoundsCheck loop:{{B\d+}}
    private static int periodicDynamicBCE(int[] x) {
        int k = 0;
        int result = 0;
        for (int i = 0; i < 10; i++) {
            result += x[k];
            k = 1 - k;
        }
        return result;
    }
    // CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before)
    // CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
    // CHECK-DAG: NullCheck   loop:<<Loop>>
    // CHECK-DAG: ArrayLength loop:<<Loop>>
    // CHECK-DAG: BoundsCheck loop:<<Loop>>
    //
    // CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
    // CHECK-DAG: ArrayGet    loop:{{B\d+}}
    // CHECK-DAG: Deoptimize  loop:none
    //
    // CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
    // CHECK-NOT: NullCheck   loop:{{B\d+}}
    // CHECK-NOT: ArrayLength loop:{{B\d+}}
    // CHECK-NOT: BoundsCheck loop:{{B\d+}}
    private static int dynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) {
        // This loop could be infinite for hi = max int. Since i is also used
        // as subscript, however, dynamic bce can proceed.
        int result = 0;
        for (int i = lo; i <= hi; i++) {
            result += x[i];
        }
        return result;
    }
    // CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before)
    // CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
    // CHECK-DAG: BoundsCheck loop:<<Loop>>
    //
    // CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
    // CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
    // CHECK-DAG: BoundsCheck loop:<<Loop>>
    //
    // CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
    // CHECK-NOT: Deoptimize
    static int noDynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) {
        // As above, but now the index is not used as subscript,
        // and dynamic bce is not applied.
        int result = 0;
        for (int k = 0, i = lo; i <= hi; i++) {
            result += x[k++];
        }
        return result;
    }
    // CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (before)
    // CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
    // CHECK-DAG: BoundsCheck loop:<<Loop>>
    //
    // CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after)
    // CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
    // CHECK-DAG: BoundsCheck loop:<<Loop>>
    //
    // CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after)
    // CHECK-NOT: Deoptimize
    static int noDynamicBCEMixedInductionTypes(int[] x, long lo, long hi) {
        int result = 0;
        // Mix of int and long induction.
        int k = 0;
        for (long i = lo; i < hi; i++) {
            result += x[k++];
        }
        return result;
    }
    // CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (before)
    // CHECK-DAG: BoundsCheck loop:<<InnerLoop:B\d+>>
    // CHECK-DAG: ArrayGet    loop:<<InnerLoop>>
    // CHECK-DAG: If          loop:<<InnerLoop>>
    // CHECK-DAG: If          loop:<<OuterLoop:B\d+>>
    // CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>"
    //
    // CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
    // CHECK-DAG: ArrayGet   loop:<<InnerLoop:B\d+>>
    // CHECK-DAG: Deoptimize loop:<<OuterLoop:B\d+>>
    // CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>"
    //
    // CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
    // CHECK-NOT: BoundsCheck
    //
    //  No additional top tests were introduced.
    // CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
    // CHECK-DAG: If
    // CHECK-DAG: If
    // CHECK-NOT: If
    static int dynamicBCEConstantRange(int[] x) {
        int result = 0;
        for (int i = 2; i <= 6; i++) {
            // Range analysis sees that innermost loop is finite and always taken.
            for (int j = i - 2; j <= i + 2; j++) {
                result += x[j];
            }
        }
        return result;
    }
    // CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (before)
    // CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop:B\d+>>
    // CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
    // CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
    //
    // CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after)
    //  Order matters:
    // CHECK:              Deoptimize loop:<<Loop:B\d+>>
    //  CHECK-NOT:          Goto       loop:<<Loop>>
    // CHECK-DAG: {{l\d+}} ArrayGet   loop:<<Loop>>
    // CHECK-DAG: {{l\d+}} ArrayGet   loop:<<Loop>>
    // CHECK-DAG: {{l\d+}} ArrayGet   loop:<<Loop>>
    // CHECK:              Goto       loop:<<Loop>>
    //
    // CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after)
    // CHECK-DAG: Deoptimize loop:none
    static int dynamicBCEAndConstantIndices(int[] x, int[][] a, int lo, int hi) {
        // Deliberately test array length on a before the loop so that only bounds checks
        // on constant subscripts remain, making them a viable candidate for hoisting.
        if (a.length == 0) {
            return -1;
        }
        // Loop that allows BCE on x[i].
        int result = 0;
        for (int i = lo; i < hi; i++) {
            result += x[i];
            if ((i % 10) != 0) {
                // None of the subscripts inside a conditional are removed by dynamic bce,
                // making them a candidate for deoptimization based on constant indices.
                // Compiler should ensure the array loads are not subsequently hoisted
                // "above" the deoptimization "barrier" on the bounds.
                a[0][i] = 1;
                a[1][i] = 2;
                a[99][i] = 3;
            }
        }
        return result;
    }
    // CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (before)
    // CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
    // CHECK-DAG: ArrayGet    loop:<<Loop>>
    // CHECK-DAG: ArrayGet    loop:<<Loop>>
    // CHECK-DAG: ArrayGet    loop:<<Loop>>
    // CHECK-DAG: ArrayGet    loop:<<Loop>>
    // CHECK-DAG: ArrayGet    loop:<<Loop>>
    // CHECK-DAG: ArrayGet    loop:<<Loop>>
    // CHECK-DAG: ArrayGet    loop:<<Loop>>
    // CHECK-DAG: ArrayGet    loop:<<Loop>>
    //  For brevity, just test occurrence of at least one of each in the loop:
    // CHECK-DAG: NullCheck   loop:<<Loop>>
    // CHECK-DAG: ArrayLength loop:<<Loop>>
    // CHECK-DAG: BoundsCheck loop:<<Loop>>
    //
    // CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
    // CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
    // CHECK-NOT: ArrayGet    loop:<<Loop>>
    //
    // CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
    // CHECK-NOT: NullCheck   loop:{{B\d+}}
    // CHECK-NOT: ArrayLength loop:{{B\d+}}
    // CHECK-NOT: BoundsCheck loop:{{B\d+}}
    //
    // CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
    // CHECK-DAG: Deoptimize  loop:none
    private static int dynamicBCEAndConstantIndicesAllPrimTypes(int[] q, boolean[] r, byte[] s, char[] t, short[] u, int[] v, long[] w, float[] x, double[] y, int lo, int hi) {
        int result = 0;
        for (int i = lo; i < hi; i++) {
            // All constant index array references can be hoisted out of the loop during BCE on q[i].
            result += q[i] + (r[0] ? 1 : 0) + (int) s[0] + (int) t[0] + (int) u[0] + v[0] + (int) w[0] + (int) x[0] + (int) y[0];
        }
        return result;
    }
    // CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (before)
    // CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
    // CHECK-DAG: NullCheck   loop:<<Loop>>
    // CHECK-DAG: ArrayLength loop:<<Loop>>
    // CHECK-DAG: BoundsCheck loop:<<Loop>>
    // CHECK-DAG: ArrayGet    loop:<<Loop>>
    // CHECK-DAG: NullCheck   loop:<<Loop>>
    // CHECK-DAG: ArrayLength loop:<<Loop>>
    // CHECK-DAG: BoundsCheck loop:<<Loop>>
    //
    // CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after)
    // CHECK-DAG: ArrayGet    loop:<<Loop:B\d+>>
    // CHECK-DAG: Deoptimize  loop:none
    //
    // CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after)
    // CHECK-NOT: ArrayLength loop:{{B\d+}}
    // CHECK-NOT: BoundsCheck loop:{{B\d+}}
    private static int dynamicBCEAndConstantIndexRefType(int[] q, Integer[] z, int lo, int hi) {
        int result = 0;
        for (int i = lo; i < hi; i++) {
            // Similar to above, but now implicit call to intValue() may prevent hoisting
            // z[0] itself during BCE on q[i]. Therefore, we just check BCE on q[i].
            result += q[i] + z[0];
        }
        return result;
    }
    //
    // Verifier.
    //
    public static void main(String[] args) {
        // Set to run expensive tests for correctness too.
        boolean heavy = false;
        int[] x = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int[] a200 = new int[200];
        // Sorting.
        int[] sort = {5, 4, 1, 9, 10, 2, 7, 6, 3, 8};
        bubble(sort);
        for (int i = 0; i < 10; i++) {
            expectEquals(sort[i], x[i]);
        }
        // Periodic adds (1, 3), one at the time.
        expectEquals(0, periodicIdiom(-1));
        for (int tc = 0; tc < 32; tc++) {
            int expected = (tc >> 1) << 2;
            if ((tc & 1) != 0)
                expected += 1;
            expectEquals(expected, periodicIdiom(tc));
        }
        // Periodic adds (1, 3), one at the time.
        expectEquals(0, periodicSequence2(-1));
        for (int tc = 0; tc < 32; tc++) {
            int expected = (tc >> 1) << 2;
            if ((tc & 1) != 0)
                expected += 1;
            expectEquals(expected, periodicSequence2(tc));
        }
        // Periodic adds (1, 3, 5, 7), all at once.
        expectEquals(0, periodicSequence4(-1));
        for (int tc = 0; tc < 32; tc++) {
            expectEquals(tc * 16, periodicSequence4(tc));
        }
        // Large bounds.
        expectEquals(55, justRightUp1());
        expectEquals(55, justRightUp2());
        expectEquals(55, justRightUp3());
        expectEquals(55, justRightDown1());
        expectEquals(55, justRightDown2());
        expectEquals(55, justRightDown3());
        sResult = 0;
        try {
            justOOBUp();
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult = 1;
        }
        expectEquals(1, sResult);
        sResult = 0;
        try {
            justOOBDown();
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult = 1;
        }
        expectEquals(1, sResult);
        // Lower bound goes OOB.
        sResult = 0;
        try {
            lowerOOB(x);
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1000, sResult);
        // Upper bound goes OOB.
        sResult = 0;
        try {
            upperOOB(x);
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1055, sResult);
        // Do while up goes OOB.
        sResult = 0;
        try {
            doWhileUpOOB();
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1055, sResult);
        // Do while down goes OOB.
        sResult = 0;
        try {
            doWhileDownOOB();
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1055, sResult);
        // Hidden OOB.
        sResult = 0;
        try {
            hiddenOOB1(10); // no OOB
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1, sResult);
        sResult = 0;
        try {
            hiddenOOB1(-2147483648); // OOB
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1001, sResult);
        sResult = 0;
        try {
            hiddenOOB2(1); // no OOB
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1, sResult);
        if (heavy) {
            sResult = 0;
            try {
                hiddenOOB2(2147483647); // OOB
            } catch (ArrayIndexOutOfBoundsException e) {
                sResult += 1000;
            }
            expectEquals(1002, sResult);
        }
        sResult = 0;
        try {
            hiddenInfiniteOOB();
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1011, sResult);
        sResult = 0;
        try {
            hiddenFiniteOOB();
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1111, sResult);
        sResult = 0;
        try {
            inductionOOB(a200);
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1000, sResult);
        for (int i = 0; i < 200; i++) {
            expectEquals(i < 128 ? i : 0, a200[i]);
        }
        sResult = 0;
        try {
            controlOOB(a200);
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1000, sResult);
        for (int i = 0; i < 200; i++) {
            expectEquals(i < 128 ? -i : 0, a200[i]);
        }
        sResult = 0;
        try {
            conversionOOB(a200);
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1000, sResult);
        for (int i = 0; i < 200; i++) {
            expectEquals(i < 128 ? i : 0, a200[i]);
        }
        // Addition.
        {
            int[] e1 = {1, 2, 3, 4, 4, 4, 4, 3, 2, 1};
            int[] a1 = add();
            for (int i = 0; i < 10; i++) {
                expectEquals(a1[i], e1[i]);
            }
        }
        // Multiplication.
        {
            int[] e1 = {7, 1, 2, 2, 1, 0, 2, 0, 0, 1};
            int[] a1 = multiply1();
            for (int i = 0; i < 10; i++) {
                expectEquals(a1[i], e1[i]);
            }
            int[] e2 = {1001, 0, 0, 1, 0, 0, 1, 0, 0, 1};
            int[] a2 = multiply2();
            for (int i = 0; i < 10; i++) {
                expectEquals(a2[i], e2[i]);
            }
        }
        // Dynamic BCE.
        sResult = 0;
        try {
            linearDynamicBCE1(x, -1, x.length);
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1000, sResult);
        sResult = 0;
        linearDynamicBCE1(x, 0, x.length);
        expectEquals(55, sResult);
        sResult = 0;
        try {
            linearDynamicBCE1(x, 0, x.length + 1);
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1055, sResult);
        // Dynamic BCE with offset.
        sResult = 0;
        try {
            linearDynamicBCE2(x, 0, x.length, -1);
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1000, sResult);
        sResult = 0;
        linearDynamicBCE2(x, 0, x.length, 0);
        expectEquals(55, sResult);
        sResult = 0;
        try {
            linearDynamicBCE2(x, 0, x.length, 1);
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult += 1000;
        }
        expectEquals(1054, sResult);
        // Dynamic BCE candidates.
        expectEquals(55, wrapAroundDynamicBCE(x));
        expectEquals(15, periodicDynamicBCE(x));
        expectEquals(55, dynamicBCEPossiblyInfiniteLoop(x, 0, 9));
        expectEquals(55, noDynamicBCEPossiblyInfiniteLoop(x, 0, 9));
        expectEquals(55, noDynamicBCEMixedInductionTypes(x, 0, 10));
        expectEquals(125, dynamicBCEConstantRange(x));
        // Dynamic BCE combined with constant indices.
        int[][] a;
        a = new int[0][0];
        expectEquals(-1, dynamicBCEAndConstantIndices(x, a, 0, 10));
        a = new int[100][10];
        expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
        for (int i = 0; i < 10; i++) {
            expectEquals((i % 10) != 0 ? 1 : 0, a[0][i]);
            expectEquals((i % 10) != 0 ? 2 : 0, a[1][i]);
            expectEquals((i % 10) != 0 ? 3 : 0, a[99][i]);
        }
        a = new int[2][10];
        sResult = 0;
        try {
            expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
        } catch (ArrayIndexOutOfBoundsException e) {
            sResult = 1;
        }
        expectEquals(1, sResult);
        expectEquals(a[0][1], 1);
        expectEquals(a[1][1], 2);
        // Dynamic BCE combined with constant indices of all types.
        boolean[] x1 = {true};
        byte[] x2 = {2};
        char[] x3 = {3};
        short[] x4 = {4};
        int[] x5 = {5};
        long[] x6 = {6};
        float[] x7 = {7};
        double[] x8 = {8};
        expectEquals(415, dynamicBCEAndConstantIndicesAllPrimTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, 0, 10));
        Integer[] x9 = {9};
        expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10));
        System.out.println("ExpectResult");
    }
    private static void expectEquals(int expected, int result) {
        if (expected != result) {
            throw new Error("Expected: " + expected + ", found: " + result);
        }
    }
}
